package net.studio.simpletetris.control;

import net.studio.simpletetris.R;
import net.studio.simpletetris.intf.IBlock;
import net.studio.simpletetris.intf.IField;
import net.studio.simpletetris.intf.IFigure;
import net.studio.simpletetris.intf.IGame;
import net.studio.simpletetris.intf.IGameListener;
import net.studio.simpletetris.intf.IGameView;
import net.studio.simpletetris.intf.IFigure.MoveDirect;
import net.studio.simpletetris.widget.GameView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.util.Log;

public class Game implements IGame, Runnable {
	public static final int PAUSE   = 0;
	public static final int READY   = 1;
	public static final int RUNNING = 2;
	public static final int LOSE   = 3;
	
	private int mode = READY;
	private Thread moveThread = null;
	
	private int level = 1;
	private long score = 0;
	private int sleep = 500;
	
	private IGameView theGameView;
	private IGameView theNextGameView;
	
	public static Game instance = null;
	
	private IGameListener listener;
	
	private Bitmap cubeBitmap;
	
	public static IGame factory(Context context, IGameView gameView, IGameView nextGameView, IGameListener listener) {
		instance = new Game(context, gameView, nextGameView, listener);
		return instance;
	}

	private Game(Context context, IGameView gameView, IGameView nextGameView, IGameListener listener) {
		this.theGameView = gameView;
		this.theNextGameView = nextGameView;
		this.listener = listener;
		this.cubeBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.cube);
	}

	public void resume() {
		if (mode != RUNNING)
		{
			mode = RUNNING;
			if (moveThread == null) {
				moveThread = new Thread(this);
				moveThread.start();
			}
		}
	}

	public void pause() {
		mode = PAUSE;
	}

	public void play() {
		if (listener != null)
			listener.onStart();
		
		theGameView.getField().setColor(0xffcccccc);
		
		nextFigure();
		
		resume();
	}
	
	public void stop() {
		mode = READY;
		if (listener != null)
			listener.onStop();
		reset();
	}
	
	public boolean isRunning() {
		return (mode == RUNNING && moveThread != null);
	}

	public void run() {
		while (isRunning()) {
			try {
				this.move(MoveDirect.DOWN);
				Thread.sleep(sleep);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		moveThread = null;
	}

	synchronized public void move(int direct) {
		if (!isRunning()) return;
		
		if (theGameView.getField().getFigure() == null) {
			if (MoveDirect.DOWN == direct) 
				nextFigure();
			return;
		}
		
		checkAndMove(direct);
	}

	synchronized public void moveExpress(int direct) {
		if (!isRunning()) return;
		
		if (theGameView.getField().getFigure() == null) {
			if (MoveDirect.DOWN == direct) 
				nextFigure();
			return;
		}
		
		checkAndMoveExpress(direct);
	}
	
	synchronized public void rotate(int direction) {
		if (!isRunning()) return;
		
		if (theGameView.getField().getFigure() == null) return;
		
		checkAndRotate(direction);
	}
	
	public void reset() {
		level = 1;
		score = 0;
		sleep = 500;
		if (listener != null)
			listener.onScore(level, score);
		
		//if (theGameView.getField() != null) {
			theGameView.getField().setFigure(null);
			for (int i = theGameView.getField().getBlockCount() - 1; i >= 0; i--)
				theGameView.getField().removeBlock(i);
		//}
	
		//if (theNextGameView.getField() != null) {
			theNextGameView.getField().setFigure(null);
			for (int i = theNextGameView.getField().getBlockCount() - 1; i >= 0; i--)
				theNextGameView.getField().removeBlock(i);
		//}
	}
	
	public void gameOver() {
		mode = LOSE;
		
		if (listener != null) {
			listener.onGameOver();
			//listener.onStop();
		}
		
		reset();
		
		//stop();
	}

	public long getScore() {
		return score;
	}

	public int getLevel() {
		return level;
	}
	
	private void nextFigure() {
		IFigure figure = theNextGameView.getField().getFigure();
		if (figure != null) {
			IFigure tmpfigure = figure.clone();
			tmpfigure.setY(tmpfigure.getY() - figure.getHeight());
			tmpfigure.setX((theGameView.getField().getCols() - Figure.MAX_BLOCK_NUM) / 2 + 1);
			theGameView.getField().setFigure(tmpfigure);
			Log.i("Game", "theGameView setFigure()");
		} else {
			theGameView.getField().setFigure(null);
			Log.i("Game", "theGameView clear figure");
		}
		
		figure = Figure.getRandomFigure();
		theNextGameView.getField().setFigure(figure);
		Log.i("Game", "theNextGameView setFigure()");
		if (listener != null)
			listener.onNewFigure(figure);
	}
	
	private void checkAndMove(int direct) {
		if (canMove(direct))
			theGameView.getField().getFigure().move(direct);
		else {
			fixAndNewFigure();
		}
	}
	
	private void checkAndMoveExpress(int direct) {
		while (true) {
			if (canMove(direct))
				theGameView.getField().getFigure().move(direct);
			else {
				fixAndNewFigure();
				break;
			}
		}
	}
	
	private void checkAndRotate(int direct) {
		if (canRotate(direct))
			theGameView.getField().getFigure().rotate(direct);
		else {
			fixAndNewFigure();
		}
	}
	
	synchronized private void fixAndNewFigure() {
		if (canMove(MoveDirect.DOWN)) return;
		
		theGameView.getField().fixFigureBlocks(theGameView.getField().getFigure());
		
		int lines = 0;
		int count = ((Field)theGameView.getField()).checkLinesToDrop();
		while(count > 0) {
			lines += count;
			count = ((Field)theGameView.getField()).checkLinesToDrop();
		}
		if (lines > 0) {
			doScore(lines);
			if (listener != null)
				listener.onScore(level, score);
		}
		
		nextFigure();

		if (((Field)theGameView.getField()).countLine(0) > 0) {
			this.gameOver();
		}
	}
	
	private void doScore(int lines) {
		if (lines >= 4)
			score += 800;
		else if (lines >= 3)
			score += 400;
		else if (lines >= 2)
			score += 200;
		else
			score += 100;
		int tmpLevel = (int)score / 2500 + 1;
		if (tmpLevel > level) {
			level = tmpLevel;
			if (sleep > 50)
				sleep -= 50;
		}
	}

	private boolean canMove(int direct) {
		IFigure tmpFigure = theGameView.getField().getFigure().clone();
		tmpFigure.move(direct);

		return !theGameView.getField().validateIntersection(tmpFigure, true);
	}
	
	private boolean canRotate(int direct) {
		IFigure tmpFigure = theGameView.getField().getFigure().clone();
		tmpFigure.rotate(direct);

		return !theGameView.getField().validateIntersection(tmpFigure, true);
	}
	
	@Override
	public void setGameListener(IGameListener listener) {
		this.listener = listener;
	}

	@Override
	public IGameListener getGameListener() {
		return listener;
	}

	public static IBlock newBlock(int x, int y, int color) {
		return new Block(x, y, color);
	}

	public static IField newField(int rows, int cols, int color) {
		return new Field(rows, cols, color);
	}

	public static IFigure newFigure(int type, int color) {
		return new Figure(type, color);
	}

	@Override
	public void drawBlock(IGameView view, Canvas canvas, IBlock block) {
		int blockSize = ((GameView)view).getBlockSize();
		int frameX = (int)((GameView)view).getFrameX();
		int frameY = (int)((GameView)view).getFrameY();
		Paint paint = new Paint();
		paint.setColor(block.getColor());
		int x = block.getX();
		int y = block.getY();
		if (block.getParent() != null) {
			x += block.getParent().getX();
			y += block.getParent().getY();
		}
		if (x < 0 || y < 0) return;
		
		int sizeX = cubeBitmap.getWidth() / 7;
		int sizeY = cubeBitmap.getHeight() / 2;
		int left = sizeX * Figure.getColorIndex(block.getColor());
		 
		Rect srcRect = new Rect(left, 0, left + sizeX - 1, sizeY - 1);
		Rect dstRect = new Rect(frameX + x * blockSize + 1, frameY + y * blockSize + 1,
				frameX + x * blockSize + blockSize, frameY + y * blockSize + blockSize);
		canvas.drawBitmap(cubeBitmap, srcRect, dstRect, paint);
		
		//canvas.drawRect(frameX + x * blockSize + 1, frameY + y * blockSize + 1, 
		//		frameX + x * blockSize + blockSize - 1, frameY + y * blockSize + blockSize - 1, paint);
	}

	@Override
	public void drawField(IGameView view, Canvas canvas, IField field) {
		int blockSize = ((GameView)view).getBlockSize();
		float frameX = ((GameView)view).getFrameX();
		float frameY = ((GameView)view).getFrameY();
		Paint paint = new Paint();
		paint.setColor(field.getColor());
		paint.setAntiAlias(true);
		paint.setDither(true);
		paint.setStrokeWidth(1f);
		paint.setStyle(Paint.Style.STROKE);
		paint.setStrokeJoin(Paint.Join.ROUND);
		paint.setStrokeCap(Paint.Cap.ROUND);
		//canvas.drawColor(Color.WHITE);
		canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
		for (int x = 0; x <= field.getCols(); x++) {
			canvas.drawLine(frameX + x * blockSize, frameY,
					frameX + x * blockSize, frameY + field.getRows() * blockSize, paint);
			for (int y = 0; y <= field.getRows(); y++) {
				canvas.drawLine(frameX, frameY + y * blockSize,
						frameX + field.getCols() * blockSize, frameY + y * blockSize, paint);
			}
		}

		for (int i = 0; i < field.getBlockCount(); i++) {
			drawBlock(view, canvas, field.getBlock(i));
		}
		
		if (field.getFigure() != null)
			drawFigure(view, canvas, field.getFigure());
		
//		if (mode == LOSE && field == this.field) {
//			paint.setColor(Color.RED);
//			canvas.drawText("Game Over", canvas.getWidth() / 2, canvas.getHeight() / 2, paint);
//		}
	}

	@Override
	public void drawFigure(IGameView view, Canvas canvas, IFigure figure) {
		for (int i = 0; i < figure.getBlockCount(); i++)
			drawBlock(view, canvas, figure.getBlock(i));
	}

	public IGameView getGameView() {
		return theGameView;
	}

	public IGameView getNextGameView() {
		return theNextGameView;
	}

}
